

// 类方法
class SQLVuex {
  // 词袋
  static language = {
    // 动词
    v: [
      "listen", // 监听
      "create", // 创建
      "set", // 增
      "delete", // 删
      "update", // 改
      "select", // 查
      "reset", // 格式化
      "fetch" // 请求远程数据
    ],
    // 判断词
    if: [
      "if", // 存在
      "if not" // 没有
    ],
    // 助动词
    vn: [
      "to",  // 去 ***
      "from" // 从 ***
    ],
    // 连携词
    a: [
      "and",  // 和 ***(同步)
      "than"  // 然后 ***(异步)
    ],
    // 名词
    n: [
      "*" // 全部的
    ]
  };
  constructor() {
    // 默认没有初始化
    this.isInit = false;
    // 存入store配置数据
    this.storeDataBase = {};
  }
  // 存入store
  setStore(store) {
    if (this.isInit) {
      console.warn("不能多次初始化vuex!");
      return;
    }
    this.store = store;
    this.isInit = true;
  }
  // 获取store
  getStore() {
    return this.store;
  }
  // 深度拷贝一个对象
  deepClone(obj) {
    let newObj = Array.isArray(obj) ? [] : {};
    if (typeof obj === "object") {
      for (let key in obj) {
        newObj[key] = (typeof obj[key] === 'object') ? this.deepClone(obj[key]) : obj[key];
      }
    }
    return newObj
  }
  // 处理语言逻辑
  _getLanguageKind(d) {
    // 默认是名词
    let kind = "n";
    Object.keys(SQLVuex.language).forEach(lan => {
      // 如果文字在词袋里那么给他分类
      if (SQLVuex.language[lan].includes(d)) kind = lan;
    });
    return kind;
  }
  nlp(sqlLanguage) {
    // nlp处理
    let nlp = [];
    // 分割
    let languageArr = sqlLanguage.split(" ");
    // 过滤多空格
    languageArr = languageArr.filter(d => !!d);
    // 语句段
    let language = [];
    // 每个语段中动词和名词
    const getAV = language => {
      let lanConfig = {
        v: "", // 动词
        n1: [], // 名词1
        vn: "", // 动名词
        n2: [] // 名词2
      };
      // 是否存在连接词
      let hasVn = false;
      // 判断动词
      language.forEach(lan => {
        if (this._getLanguageKind(lan) === "vn") {
          lanConfig.vn = lan;
          hasVn = true;
        } else if (this._getLanguageKind(lan) === "v") {
          lanConfig.v = lan;
        } else if (this._getLanguageKind(lan) === "n") {
          lan = lan.split(".");
          if (!hasVn) lanConfig.n1.push(...lan);
          else lanConfig.n2.push(...lan);
        }
      });
      return lanConfig;
    };
    // 分割语段
    languageArr.forEach((d, i) => {
      // 最后一条数据
      if (i === languageArr.length - 1) {
        // than ==> 异步语句
        language.push(d);
        nlp.push({
          async: false,
          ...getAV(language)
        });
        language = [];
        return;
      }
      // 获取连携数量
      if (this._getLanguageKind(d) === "a") {
        // 判断and右是不是名词
        if (d === "and" && this._getLanguageKind(languageArr[i + 1]) === "n") {
          // 判断词性
          language.push(d);
        } else {
          // than ==> 异步语句
          nlp.push({
            async: d === "than",
            ...getAV(language)
          });
          language = [];
        }
      } else {
        language.push(d);
      }
    });
    // 判断连携逻辑
    return nlp;
  }
  // 获取所有TYPE
  _getAllMutations(lan, config, defaultState) {
    // 获取store
    const store = this.getStore();
    const n1 = lan.n1.join("."); // 字段
    // const n2 = lan.n2.join("."); // 什么数据库
    Object.keys(config.state).forEach(key => {
      // 对数据库每个字段创建action type
      // 动态创建action tppe
      const typeSet = `SET_${key}_TO_${n1}`.toUpperCase();
      defaultState.mutations[typeSet] = (state, { data, lan }) => {
        // 改变js数据结构
        lan.n1.reduce((add, key, i) => {
          if (i === lan.n1.length - 1) {
            add[key] = data;
          } else {
            add = state[key];
          }
        }, state);
      };
      // 存入所有字段到表内(合并)
      const typeSetAll = `SET_*_TO_${n1}`.toUpperCase();
      defaultState.mutations[typeSetAll] = (state, { data }) => {
        Object.keys(data).forEach(key => {
          state[key] = data[key];
        });
      };

      // delete
      // 判断跟节点能不能删除
      const typeUnresgister = `UNREGISTER_${n1}`.toUpperCase();
      if (!defaultState.mutations[typeUnresgister]) {
        defaultState.mutations[typeUnresgister] = (state, { lan }) => {
          // 改变js数据结构
          store.unregisterModule(lan.n1);
        };
      }
      const typeDelete = `DELETE_${key}_FROM_${n1}`.toUpperCase();
      defaultState.mutations[typeDelete] = (state, { lan }) => {
        // 改变js数据结构
        lan.n1.reduce((add, key, i) => {
          if (i === lan.n1.length - 1) {
            delete add[key];
          } else {
            add = state[key];
          }
        }, state);
      };
      // reset
      // 初始化state
      const typeResetAll = `RESET_${n1}`.toUpperCase();
      if (!defaultState.mutations[typeResetAll]) {
        defaultState.mutations[typeResetAll] = (state, { lan }) => {
          // 初始化表数据
          const defData = this.storeDataBase[lan.n1.join(".")];
          if (defData) {
            // 初始化状态机
            Object.keys(defData.state).forEach(key => {
              state[key] = defData.state[key];
            });
            // 删除多余字段
            Object.keys(state).forEach(key => {
              if (!Object.keys(defData.state).includes(key)) {
                delete state[key];
              }
            });
          } else {
            console.warn("丢失初始化state数据:" + lan.n1.join("."));
          }
        };
      }
      // 单个字段初始化
      const typeReset = `RESET_${key}_FROM_${n1}`.toUpperCase();
      defaultState.mutations[typeReset] = (state, { lan }) => {// 初始化表数据
        const defTable = this.storeDataBase[n1];
        let defData = defTable.state;
        // 查找字段数据
        key.split(".").forEach(key => {
          if (defData[key]) defData = defData[key];
        });
        if (defData) {
          // 改变js数据结构
          lan.n1.reduce((add, key, i) => {
            if (i === lan.n1.length - 1) {
              add[key] = defData;
            } else {
              add = state[key];
              defData = defData[key];
            }
          }, state);
        } else {
          console.warn("丢失初始化state数据:" + lan.n1.join("."));
        }
      };

    });
    return defaultState;
  }
  // 获取所有_getAllGetter
  _getAllGetters(lan, config, defaultState) {
    // 默认getter获取所有状态
    defaultState.getters.$sql_getState = state => state;
    // 创建getters
    Object.keys(config.state).forEach(key => {
      defaultState.getters[`${key}`] = state => state[key];
    });
    return defaultState;
  }
  // store操作
  async vuexAction(languages, config) {
    // 获取store
    const store = this.getStore();
    // store事件操作
    const action = {
      // 创建表
      create: (lan, config = { state: {} }) => {
        // create 只有n1名词
        let defaultState = {
          namespaced: true,
          state: {}, // 默认状态机
          getters: {}, // getter
          mutations: {}, // mutations
          actions: {}, // actions
          ...config
        };
        // 自动创建getters
        defaultState = this._getAllGetters(lan, config, defaultState);
        // 自动创建type到mutations上
        defaultState = this._getAllMutations(lan, config, defaultState);
        // 存入缓存对象中
        this.storeDataBase[lan.n1.join(".")] = this.deepClone(defaultState);
        // 挂到store上
        store.registerModule(lan.n1, defaultState);
      },
      // 存入
      set: (lan, config, up) => {
        const n1 = lan.n1.join(".") || "*"; // 字段
        const n2 = lan.n2.join("."); // 什么数据库
        // 用上个语句输出做为输入
        if (up) config = up;
        // 动态创建action tppe
        const type = `SET_${n1}_TO_${n2}`.toUpperCase();
        // 触发action type
        store.commit(`${lan.n2.join("/")}/${type}`, { data: config, lan });
      },
      // 改写 === set
      update: function (lan, config) {
        this.set(lan, config);
      },
      // 删除
      delete: lan => {
        const n1 = lan.n1.join("."); // 字段
        const n2 = lan.n2.join("."); // 什么数据库
        // 触发action type
        if (lan.n2.length !== 0) {
          // 删除数据库字段
          // 动态创建action tppe
          const type = `DELETE_${n1}_FROM_${n2}`.toUpperCase();
          store.commit(`${lan.n2.join("/")}/${type}`, { lan });
        } else {
          // 删除数据库
          // 动态创建action tppe
          const type = `UNREGISTER_${n1}`.toUpperCase();
          store.commit(`${lan.n1.join("/")}/${type}`, { lan });
        }
      },
      // 格式化
      reset: lan => {
        // 判断是否存在这个库没的话直接返回
        const n1 = lan.n1.join("."); // 字段
        const n2 = lan.n2.join("."); // 什么数据库
        if (!n2) {
          if (!this.storeDataBase[n1]) return; // 没数据直接返回(用于容错)
        } else {
          if (!this.storeDataBase[n2]) return; // 没数据直接返回(用于容错)
        }
        // 触发action type
        if (lan.n2.length !== 0) {
          // 删除数据库字段
          // 动态创建action tppe
          const type = `RESET_${n1}_FROM_${n2}`.toUpperCase();
          store.commit(`${lan.n2.join("/")}/${type}`, { lan });
        } else {
          // 删除数据库
          // 动态创建action tppe
          const type = `RESET_${n1}`.toUpperCase();
          store.commit(`${lan.n1.join("/")}/${type}`, { lan });
        }
      },
      // 查询逻辑
      select: lan => {
        if (lan.n2.length !== 0) {
          // 获取整个table数据
          return store.getters[`${lan.n2.join("/")}/${lan.n1.join(".")}`];
        } else {
          // 获取整个table数据
          return store.getters[`${lan.n1.join("/")}/$sql_getState`];
        }
      },
      // 请求
      fetch: async (lan, config) => {
        const url = lan.n1.join(".");
        const res = await fetch(url, config);
        const json = await res.json();
        return json;
      }
    };
    // 执行语言任务
    let ret = [];
    // 多语句执行
    for (let i = 0; i < languages.length; i++) {
      const lan = languages[i];
      const lanUp = languages[i - 1];
      if (lanUp && lanUp.async) {
        const up = await ret[i - 1];
        await (() => new Promise((resolve, reject) => {
          try {
            // 同步
            ret.push(action[lan.v](lan, config ? config[i] : {}, up));
            resolve();
          } catch (e) {
            reject(e);
          }
        }))();
      } else {
        // 同步
        ret.push(action[lan.v](lan, config ? config[i] : {}));
      }
    }
    return ret;
  }
  // 运行
  run(sqlLanguage) {
    // 获取nlp数据
    const language = this.nlp(sqlLanguage);
    return {
      language, // nlp处理
      start: (config = {}) => this.vuexAction(language, config)
    };
  }
}

export default new SQLVuex
